/* 	This file is free to modify. You MUST submit this file.
*/
package MP.staticchecker;

import MP.MPC;
import MP.lexicalanalysis.Token;
import MP.CompilationException;
import java.util.*;

class MaHash
{
	int indexNext;//chi so cua bien nam o bang SymHashTable va la next cua entry
	int indexHash;//chua chi so ma bam
	MaHash(int in,int inhash)
	{
		indexNext = in;
		indexHash = inhash;
	}
} 

public class SymHashTable extends SymTable{
	
	Stack<SymHashEntry> tab = new Stack<SymHashEntry>();
	Stack<Integer> scope = new Stack<Integer>();
	ArrayList index = new ArrayList();
	ArrayList tempPara = new ArrayList();//chi sai tam de bo cac thong so cho ham cua ngon ngu
	int currentScope;
	int indexTable;//chi so cua entry trong tab (entrycuoi + 1)
	int countArray;//chi so cua 1 MaHash tron bang bam (ArrayList)
	int chiso;//chi so MaHash trong ArrayList
	int func;

	int hash(String s) {
		return MPC.hash(s);
	}
	public SymHashTable()
	{
		indexTable = 0;
		chiso = 0;
		countArray = 0;		
		currentScope = -1;
		enterScope();		
		init();
		//countInit = 1;		
	}
	public void init() {
		insertId(new SymHashEntry(new Token(Token.ID,"readInt")
				,new FunctionType(null,new IntegerType())));
		insertId(new SymHashEntry(new Token(Token.ID,"readReal"),new FunctionType(null,new RealType())));
		insertId(new SymHashEntry(new Token(Token.ID,"readBool"),new FunctionType(null,new BooleanType())));
		insertId(new SymHashEntry(new Token(Token.ID,"writeLn"),new FunctionType(null,new VoidType())));
		tempPara.add(0,new IntegerType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeInt"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new IntegerType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeIntLn"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new RealType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeReal"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new RealType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeRealLn"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new BooleanType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeBool"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new BooleanType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeBoolLn"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new StringType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeStr"),new FunctionType(new ParaType(tempPara),new VoidType())));
		tempPara.clear();
		tempPara.add(0,new StringType());
		insertId(new SymHashEntry(new Token(Token.ID,"writeStrLn"),new FunctionType(new ParaType(tempPara),new VoidType())));
		//tempPara.clear();
	}
	
	public void enterScope()
	{
		currentScope = indexTable;		
		scope.push((Integer)currentScope);
		
	}
	
	public MaHash getMaHash(ArrayList day, int hash)
	{
		for(int i = day.size() - 1; i > 0; i--)
		{
			MaHash temp =(MaHash) day.get(i);
			//System.out.println(temp.indexHash+" "+hash);
			if(hash == temp.indexHash)
			{
				chiso = i;
				return temp;
			}
		}
		return null;
	}
	
	public void exitScope() throws CompilationException
	{		
		int cScope = scope.peek();
		func = 0;
		for(int i = tab.size() - 1; i >= cScope; i-- )
		{
			if(tab.empty())
				throw new IllegalOperationException();
			SymHashEntry entry = (SymHashEntry) tab.pop();
			int h = hash(entry.getLexeme());
			//index.set(h,entry.getNext());
			MaHash tempHash = getMaHash(index,h);
			if(tempHash != null)
			{
				int in = tempHash.indexNext;
			tempHash.indexNext = entry.getNext();
			index.set(chiso,tempHash);
			chiso = 0;//reset chiso
			}
			
		}
		scope.pop();
		this.currentScope = cScope;
		indexTable = tab.size();
	}
	
	public Object insertId(SymEntry sym)
	{		
		SymHashEntry symtemp = (SymHashEntry) sym;
		Stack<SymHashEntry> temptab = new Stack<SymHashEntry>();
		int h = 0;
		h = hash(symtemp.getLexeme());
		int cScope = scope.peek();
		
		/*System.out.println(""+indexTable);
		System.out.println("cScope : "+cScope);
		System.out.println("Lexeme : "+ symtemp.getLexeme());
		System.out.println("h : " + h);*/
				
		//chua day sym vao		
		MaHash tempHash = getMaHash(index,h);//xu ly tempHash == null;		
		if(tempHash == null)
		{
			MaHash tempH = new MaHash(indexTable,h);
			index.add(countArray,tempH);
			++countArray;
			tab.push(symtemp);
			indexTable = tab.size();
			chiso = 0;//reset chi so
		}
		else
		{
			//System.out.println(" : " + tempHash.indexNext);
		int in = tempHash.indexNext;
		
		for( int i = in ; i >= cScope;)
		{
			//removeEntry(tab,i,temptab);
			int length = tab.size() - 1;
			for(int j = length; j > i ; j--)
			{
				temptab.push((SymHashEntry) tab.pop());
			}
			
			SymHashEntry temp = (SymHashEntry) tab.peek();
			//reset(tab,temptab);
			while(!temptab.empty())
			{
				tab.push(temptab.pop());
			}
			
			//System.out.println(temp.getLexeme());
			
			if(!(temp.getLexeme().equals(sym.getLexeme())))
				i = temp.getNext();
			else
				return temp;
		}
		symtemp.setNext(in);
		tab.push(symtemp);
		indexTable = tab.size();
		tempHash.indexNext = indexTable-1;
		index.set(chiso, tempHash);
		chiso = 0;//reset chi so
		}
		return null;
	}
	
	public SymEntry lookup(String lexeme) 
	{
		//System.out.println(lexeme);				
				
		Stack<SymHashEntry> temptab = new Stack<SymHashEntry>();
		while(!tab.isEmpty())
		{			
			SymHashEntry temp = (SymHashEntry) tab.peek();		
			
			if(temp.getLexeme().equals(lexeme))
			{
				//reset(tab,temptab);
				while(!temptab.empty())
				{
					tab.push(temptab.pop());
				}
				return temp;
			}
			else
				temptab.push(tab.pop());			
		}
		//reset(tab,temptab);
		while(!temptab.empty())
		{
			tab.push(temptab.pop());
		}
		return null;
	}
	
	public int getScope(String lexeme)
	{
		Stack<SymHashEntry> temptab = new Stack<SymHashEntry>();
		SymHashEntry temp = (SymHashEntry) lookup(lexeme);
		int indexTab = tab.size() - 1;
		while(tab.peek() != temp)
		{
			temptab.push((SymHashEntry) tab.pop());
			--indexTab;
		}
		//reset(tab,temptab);
		while(!temptab.empty())
		{
			tab.push((SymHashEntry) temptab.pop());
		}
			
		Stack<Integer> tempScope = new Stack<Integer>();
		while(indexTab < scope.peek())
		{
			tempScope.push((Integer) scope.pop());
		}
		int a = scope.peek();
		//reset(scope,tempScope);
		while(!tempScope.empty())
		{
			scope.push(tempScope.pop());
		}
			
		return a;
	}
	
	public boolean inFunctionScope(String funcName)
	{
		int scopeFunc = getScope(funcName);
		if(currentScope > scopeFunc)
			return true;
		else
			return false;
	}
	
	public boolean getIdRoot(String lexeme)
	{
		Stack<Integer> tempScope = new Stack<Integer>();
		Stack<SymHashEntry> temptab = new Stack<SymHashEntry>();
		int scope1 = 0;
		
		//lay tam vuc gan ngoai cung
		while(scope.size() != 1)
		{
			if(scope.peek() == 2)
				scope1 = scope.peek();
			tempScope.push(scope.pop());
		}
		
		//reset scope
		while(!tempScope.isEmpty())
		{
			scope.push(tempScope.pop());
		}
		//lay cac id o tam vuc goc		
		while((tab.size() - 1) != scope1)
		{
			temptab.push(tab.pop());
		}
		temptab.push(tab.pop());
		
		SymEntry entry = lookup(lexeme);
		
		//reset tab
		while(!(temptab.isEmpty()))
		{
			tab.push(temptab.pop());
		}
		
		if(entry == null)
			return false;
		else if(entry.getType() instanceof FunctionType)
			return false;
			else if(entry.getType() instanceof ArrayType)
				return false;
				else
					return true;	
	}
}